
/**
 * 大容器的标识，用于 main 里面的注入
 */
export type IStoreKey = string | symbol // | InjectionKey

/**
 * 对象 key 的 类型
 */
export type IStateKey = string | number | symbol

/**
 * 任意一个对象
 */
export type IAnyObject = {[key: IStateKey]: any}

/**
 * 任意一个数组
 */
export type IAnyArray = Array<any>

/**
 * 任意一个函数，无返回
 */
export type IAnyFunction = (...arg: Array<any>) => void

/**
 * 任意一个函数，返回对象
 */
export type IAnyFunctionObject = (...arg: Array<any>) => IAnyObject

/**
 * 任意一个函数，返回数组
 */
export type IAnyFunctionArray = (...arg: Array<any>) => IAnyArray

/**
 * 对象或者函数
 */
export type IObjectOrFunction = IAnyObject | IAnyFunctionObject

/**
 * 数组或者函数
 */
export type IArrayOrFunction = IAnyArray | IAnyFunctionArray

/**
 * 对象或者数组
 */
export type IObjectOrArray = IAnyObject | IAnyArray

/**
 * 内部保存状态的容器
 */
export interface IStore {
  [key: IStoreKey] : IState | object
}


/**
 * 对象和数组的基类，要实现的函数
 * * 私有成员
 * * * get $id —— 获取ID、状态标识，string | symbol
 * * * get $value —— 获取原值，可以是对象、数组，也可以是 function 
 * * * get $isLog —— 获取是否记录日志。true ：记日志；false： 不记日志（默认值）
 * * * get $isState —— 验证是否状态
 * * * get $isObject —— 验证是否用了对象基类
 * * * get $isArray —— 验证是否用了数组基类
 * * 内置方法
 * * * $reset() —— 重置
 * * * async $patch() —— 修改部分属性
 * * * set $state —— 整体赋值，会去掉原属性
 * * * $toRaw() —— 取原型，不包含内部方法
 * * * get $log —— 获取日志
 * * * $clearLog() —— 清空日志
 * * * 
*/
export interface IState {
  /**
   * 状态标识，string | symbol
   */
  get $id(): IStateKey;
  /**
   * 记录原值，可以是对象，也可以是 function 
   */
  get $value(): IObjectOrFunction;
  /**
   * 获取是否记录日志。true ：记日志；false： 不记日志（默认值）
   */
  get $isLog(): boolean;
  /**
   * 设置是否写日志）
   */
  set $isLog(val: boolean);
  /**
   * 验证是不是充血实体类的状态
   */
  get $isState(): boolean;
  /**
   * 验证是不是有辅助工具，区分普通的对象
   */
  get $isObject(): boolean;
  /**
   * 验证是不是有辅助工具，区分普通的数组
   */
  get $isArray(): boolean;
  /**
   * 重置，恢复初始值。函数的情况支持多层
   */
  $reset(): void;
  /**
   * 修改部分属性
   */
  $patch(_val: IObjectOrFunction): void;
  /**
   * 整体赋值，不会增加新属性
   */
  // set $state(value: any);
  /**
   * 取原型，去掉内部方法
   */
  $toRaw<T extends IAnyObject>(): T | T[];
  /**
   * 获取日志
   */
  get $logs(): Array<IStateLogInfo>;
  /**
   * 清空日志
   */
  $clearLog(): void;
  /**
   * 可以有扩展属性
   */
  [key: IStateKey] : any;
}

/**
 * 日志的类型
 * * {
 * *   time: '时间戳',
 * *   kind: '', // 操作类型
 * *   oldValue: {},
 * *   newValue: {},
 * *   subValue: {}, // 参数
 * *   callFun: '' // 调用的函数名
 * * }
 */
export interface IStateLogInfo {
  time: number;
  time2?: string;
  kind: string; // 操作类型
  oldValue: any;
  newValue: any;
  subValue: any; // 参数
  callFun: string | undefined // 调用的函数名
}

/**
 * 记录状态的变化日志容器，用key来区分
 * * stateLog = {
 * *  key: {
 * *   log: [
 * *     {
 * *       time: '时间戳',
 * *       kind: '', // 操作类型
 * *       oldValue: {},
 * *       newValue: {},
 * *       subValue: {}, // 参数
 * *       callFun: '' // 调用的函数名
 * *     }
 * *   ]
 * *  }
 * * }
 */
export interface IStateLog {
  [index: string | symbol] : {
    log: Array<IStateLogInfo>
  }
}

/**
 * 创建状态的选项。
 * * isLocal —— true：局部状态；false：全局状态（默认属性）；
 * * isLog ——  true：做记录；false：不用做记录（默认属性）；
 */
export interface IStateOption {
  /**
   * true：局部状态；false：全局状态（默认属性）；
   * 容易乱，不使用了。
   */
  // isLocal?: boolean; 
  /**
   * true：做记录；false：不用做记录（默认属性）；
   */
  isLog?: boolean; // 
  /**
   * 去掉，改为 option API 和 setup 的方式实现不同的安全级别
   * 宽松： option API；
   * 其他：采用 setup 的方式实现限制。
   * 
   * 1：宽松，可以各种改变属性，适合弹窗、抽屉、多tab切换等。
   * 2：一般，不能通过属性直接改状态，只能通过内置函数、action 改变状态
   * 3：严格，不能通过属性、内置函数改状态，只能通过 action 改变状态
   * 4：超严，只能在指定组件内改变状态，比如当前用户的状态，只能在登录组件改，其他组件完全只读
  */
  // level?: number
}


/**
 * 创建一个状态的参数类型
 * * state：状态，对象、数组，或者函数
 * * getters?：变成 computed 的对象集合
 * * actions?: 变成 action 的对象集合
 * * options?: 选项
 * * * isLog ——  true：做记录；false：不用做记录（默认属性）；
 */
export interface IStateCreateOption {
  state: IObjectOrFunction;
  getters?: IAnyObject;
  actions?: IAnyObject;
  options?: IStateOption;
}

/**
 * 创建多个全局状态的类型
 */
export interface IStateCreateListOption {
  store: {
    [key: IStateKey]: IStateCreateOption | IAnyFunctionObject;
  },
  init: (store: IStore) => void;
}
